import { ChargingStation } from '@app/db/entities/ChargingStation';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { DeviceService } from '../device/device.service';
import { CommonService } from '../common/common.service';
import { PricingFastCharge } from '@app/db/entities/PricingFastCharge';
import { PricingSlowCharge } from '@app/db/entities/PricingSlowCharge';
import { UtilsService } from '@app/utils';

@Injectable()
export class ChargingStationService {
    constructor(
        @InjectRepository(ChargingStation) protected repository:Repository<ChargingStation>,
        @InjectRepository(PricingFastCharge) protected pricingFastCharge:Repository<PricingFastCharge>,
        @InjectRepository(PricingSlowCharge) protected pricingSlowCharge:Repository<PricingSlowCharge>,
        private readonly utils:UtilsService,
        private readonly device:DeviceService,
        private common:CommonService
    ){}

    async station(id:number) {
        const res:any = {}
        const station = await this.repository.findOne({
            where : {id},
            select : ['name', 'pics', 'latitude', 'longitude', 'address', 'labels', 'fastPricing', 'slowPricing']
        })
        if(!station) return null
        res.station = station

        if(station.labels != null && station.labels.length > 0) {
            const labels = await this.common.queryChargingStationLabels()
            station.labels.forEach((label, i) => {
                station.labels[i] = labels[label]
            })
        }

        if(station.fastPricing > 0) {
            res.fastPricing = await this.pricingFastCharge.findOne({where : {id : station.fastPricing}, select : ['rules']})
        }
        if(station.slowPricing > 0) {
            res.slowPricing = await this.pricingSlowCharge.findOne({where : {id : station.slowPricing}, select : ['service', 'price']})
        }

        return res
    }
    

    async near(lng:number, lat:number, distance:number) {
        const sql = "SELECT id,fast_pricing as fastPricing,slow_pricing as slowPricing,longitude,latitude,name, labels, address,6371 * ACOS(COS(RADIANS(?)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS(?)) + SIN(RADIANS(?)) * SIN(RADIANS(latitude))) AS distance FROM charging_station HAVING distance <= ? ORDER BY distance"
        const stations:(ChargingStation & {pricing?:any, fast?:{total:number, free : number}, slow?:{total:number, free : number}})[] = await this.repository.query(sql, [lat, lng, lat, distance])
        if(stations.length > 0) {
            const stationIds:number[] = []
            const labels = await this.common.queryChargingStationLabels()
            const fastPricingIds:number[] = []
            const slowPricingIds:number[] = []
            let fastPricings:PricingFastCharge[] = []
            let slowPricings:PricingSlowCharge[] = []


            stations.forEach(station => {
                station.fast = {
                    total : 0,
                    free : 0
                }
                station.slow = {
                    total : 0,
                    free : 0
                }

                if(station.labels != null) {
                    station.labels.forEach((label, i) => {
                        station.labels[i] = labels[label]
                    })
                }

                stationIds.push(station.id)

                if(station.fastPricing > 0 && !fastPricingIds.includes(station.fastPricing)) {
                    fastPricingIds.push(station.fastPricing)
                }
                if(station.fastPricing > 0 && !slowPricingIds.includes(station.fastPricing)) {
                    slowPricingIds.push(station.fastPricing)
                }
            })

            if(fastPricingIds.length > 0) {
                fastPricings = await this.pricingFastCharge.find({
                    where : {id : fastPricingIds.length == 1 ? fastPricingIds[0] : In(fastPricingIds)},
                    select : ['rules', 'id']
                })
            }

            if(slowPricingIds.length > 0) {
                slowPricings = await this.pricingSlowCharge.find({
                    where : {id : slowPricingIds.length == 1 ? slowPricingIds[0] : In(slowPricingIds)},
                    select : ['price', 'service', 'id']
                })
            }
            

            const devices = await this.device.find({stationId : stationIds.length == 1 ? stationIds[0] : In(stationIds)}, ['chargeType', 'portCount', 'portStatus', 'stationId'])
            
            const date = new Date()

            const fastPricingMap:{[key:string]:any} = {}
            const slowPricingMap:{[key:string]:PricingSlowCharge} = {}
            fastPricings.forEach(item => {
                if(item.rules != null && item.rules.length > 0) {
                    for(let i = 0; i < item.rules.length; i ++) {
                        const rule = item.rules[i]
                        if(this.utils.isWithinTimeRange(date, rule.time)) {
                            fastPricingMap[item.id] = rule
                        }
                    }
                }
            })

            slowPricings.forEach(item => {
                slowPricingMap[item.id] = item
            })

            stations.forEach(station => {
                if(station.fastPricing > 0 && fastPricingMap[station.fastPricing] != undefined) {
                    station.pricing = fastPricingMap[station.fastPricing]
                }else if(station.slowPricing > 0 && slowPricingMap[station.slowPricing] != undefined) {
                    station.pricing = slowPricingMap[station.slowPricing]
                }

                devices.forEach(dev => {
                    if(dev.stationId == station.id) {
                        if(dev.chargeType == 1) {
                            station.fast.total += dev.portCount
                            if(dev.portStatus != null) {
                                dev.portStatus.forEach(s => {
                                    if(s == 0) {
                                        station.fast.free += 1
                                    }
                                })
                            }
                        }else if(dev.chargeType == 2){
                            station.slow.total += dev.portCount
                            if(dev.portStatus != null) {
                                dev.portStatus.forEach(s => {
                                    if(s == 0) {
                                        station.slow.free += 1
                                    }
                                })
                            }
                        }
                    }
                })
            })
        }


        return stations
    }
}
